This is my notebook that I will used to go through the tidy text notebook by Julia Silge and David Robinson.
library(tidyverse)
library(tidytext)
library(stringr)
library(janeaustenr)
text <- c("Because I could not stop for Death - ",
"He kindly stopped for me -",
"The Carriage held but just Ourselves -",
"and Immortality")
text
[1] "Because I could not stop for Death - " "He kindly stopped for me -" "The Carriage held but just Ourselves -" "and Immortality"
text_df <- data_frame(line = 1:4, text = text)
text_df
text_df %>%
unnest_tokens(word, text) # to_lower = FALSE to keep uppercase
Working with Jane Austen Books
Using the Jane Austen dataset we can make it tidy. First we’ll use mutate to create a column from existing data. Annotate line numbers and then keep track of chapters using regex.
original_books <- austen_books() %>%
group_by(book) %>%
mutate(linenumber = row_number(),
chapter = cumsum(str_detect(text, regex("^chapter [\\divxlc]",
ignore.case = TRUE)))) %>%
ungroup()
original_books
Restructure it in one-token-per-row format with the unnest_tokens() function.
tidy_books <- original_books %>%
unnest_tokens(word, text)
tidy_books
Removing stop words
We can remove stop words
data(stop_words)
tidy_books <- tidy_books %>%
anti_join(stop_words)
Joining, by = "word"
tidy_books %>%
count(word, sort = TRUE)
Plotting data
tidy_books %>%
count(word, sort = TRUE) %>%
filter(n > 600) %>%
mutate(word = reorder(word, n)) %>%
ggplot(aes(word, n))+
geom_col()+
xlab(NULL)+
coord_flip()+
theme_bw()

Gutenbergr
HG wells books
library(gutenbergr)
hgwells <- gutenberg_download(c(35, 36, 5230, 159))
Determining mirror for Project Gutenberg from http://www.gutenberg.org/robot/harvest
Using mirror http://aleph.gutenberg.org
tidy_hgwells <- hgwells %>%
unnest_tokens(word, text) %>%
anti_join(stop_words)
Joining, by = "word"
tidy_hgwells %>%
count(word, sort = TRUE)
Most commone words in novels by Bronte sisters
bronte <- gutenberg_download(c(1260, 768, 969, 9182, 767))
tidy_bronte <- bronte %>%
unnest_tokens(word, text) %>%
anti_join(stop_words)
Joining, by = "word"
tidy_bronte %>%
count(word, sort = TRUE)
Comparing Bronte and HG Wells
frequency <- bind_rows(mutate(tidy_bronte, author = "Bronte Sisters"),
mutate(tidy_hgwells, author = "H.G. Wells"),
mutate(tidy_books, author = "Jane Austen")) %>%
mutate(word = str_extract(word, "[a-z']+")) %>%
count(author, word) %>%
group_by(author) %>%
mutate(proportion = n / sum(n)) %>%
select(-n) %>%
spread(author, proportion) %>%
gather(author, proportion, `Bronte Sisters`:`H.G. Wells`)
Graphing the comparisons
library(scales)
Attaching package: ‘scales’
The following object is masked from ‘package:purrr’:
discard
The following object is masked from ‘package:readr’:
col_factor
ggplot(frequency, aes(x = proportion, y = `Jane Austen`,
color = abs(`Jane Austen` - proportion))) +
geom_abline(color = "gray40", lty = 2) +
geom_jitter(alpha = 0.1, size = 2.5, width = 0.3, height = 0.3) +
geom_text(aes(label = word, check_overlap = TRUE, vjust = 1.5))+
scale_x_log10(labels = percent_format()) +
scale_y_log10(labels = percent_format()) +
scale_color_gradient(limits = c(0,0.001),
low = "darkslategray4", high = "gray75") +
facet_wrap(~author, ncol = 2) +
theme(legend.position = "none") +
labs(y = "Jane Austen", x = NULL)
Ignoring unknown aesthetics: check_overlap

NA
Correlation analysis
cor.test(data = frequency[frequency$author == "Bronte Sisters",],
~ proportion + `Jane Austen`)
Pearson's product-moment correlation
data: proportion and Jane Austen
t = 119.65, df = 10404, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
0.7527846 0.7689620
sample estimates:
cor
0.7609915
cor.test(data = frequency[frequency$author == "H.G. Wells",],
~ proportion + `Jane Austen`)
Pearson's product-moment correlation
data: proportion and Jane Austen
t = 36.441, df = 6053, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
0.4032800 0.4445987
sample estimates:
cor
0.4241601
Sentiments dataset
library(tidytext)
sentiments
Sentiment scores
# AFINN
get_sentiments("afinn")
# Bing
get_sentiments("bing")
# NRC
get_sentiments("nrc")
Sentiment analysis can be performed with an inner join
tidy_books <- austen_books() %>%
group_by(book) %>%
mutate(linenumber = row_number(),
chapter = cumsum(str_detect(text, regex("^chapter [\\divxlc]",
ignore_case = TRUE)))) %>%
ungroup() %>%
unnest_tokens(word, text)
tidy_books
Get a list of words that are associated with ‘joy’ and perform an inner_join to find words that are have ‘joy’ sentiment within Jane Austen’s Emma book.
# Filter words associated with 'joy'
nrcjoy <- get_sentiments("nrc") %>%
filter(sentiment == "joy")
# Find words associated with joy in Emma
tidy_books %>%
filter(book == "Emma") %>%
inner_join(nrcjoy) %>%
count(word, sort = TRUE)
Joining, by = "word"
Count up positive or negative words that are within sections of each book. We will define an index to keep track of where we are in the narrative. The index will count up sections of 80 lines of text
janeaustensentiment <- tidy_books %>%
inner_join(get_sentiments("bing")) %>%
count(book, index = linenumber %/% 80, sentiment) %>%
spread(sentiment, n, fill =0) %>%
mutate(sentiment = positive - negative)
Joining, by = "word"
janeaustensentiment
ggplot(janeaustensentiment, aes(index, sentiment, fill = book)) +
geom_col(show.legend = FALSE) +
facet_wrap(~book, ncol = 2, scales = "free_x")

pride_prejudice <- tidy_books %>%
filter(book == "Pride & Prejudice")
pride_prejudice
afinn <- pride_prejudice %>%
inner_join(get_sentiments("afinn")) %>%
group_by(index = linenumber %/% 80) %>%
summarise(sentiment = sum(score)) %>%
mutate(method = "AFINN")
Joining, by = "word"
afinn
bing_and_nrc <- bind_rows(
pride_prejudice %>%
inner_join(get_sentiments("bing")) %>%
mutate(method = "Bing et al."),
pride_prejudice %>%
inner_join(get_sentiments("nrc") %>%
filter(sentiment %in% c("positive",
"negative"))) %>%
mutate(method = "NRC")) %>%
count(method, index = linenumber %/% 80, sentiment) %>%
spread(sentiment, n, fill = 0) %>%
mutate(sentiment = positive - negative)
Joining, by = "word"
Joining, by = "word"
bing_and_nrc
Comparing three sentiment lexicons using Pride and Prejudice
bind_rows(afinn,
bing_and_nrc) %>%
ggplot(aes(index, sentiment, fill = method)) +
geom_col(show.legend = FALSE) +
facet_wrap(~method, ncol = 1 , scales = "free_y")

get_sentiments("nrc") %>%
filter(sentiment %in% c("positive",
"negative")) %>%
count(sentiment)
get_sentiments("bing") %>%
count(sentiment)
bing_words_counts <- tidy_books %>%
inner_join(get_sentiments("bing")) %>%
count(word, sentiment, sort = TRUE) %>%
ungroup()
Joining, by = "word"
bing_words_counts
bing_words_counts %>%
group_by(sentiment) %>%
top_n(10) %>%
ungroup() %>%
mutate(word = reorder(word, n)) %>%
ggplot(aes(word, n, fill = sentiment)) +
geom_col(show.legend = FALSE)+
facet_wrap(~sentiment, scales = "free_y") +
labs(y = "Contribution to sentiment",
x = NULL,
title = "Top 10 words by Sentiment for Pride and Prejudice") +
coord_flip()
Selecting by n

Miss is referred to as a negative word but is not in Jane Austen’s works.
custom_stop_words <- bind_rows(data_frame(word = c("miss"),
lexicon = c("custom")),
stop_words)
custom_stop_words
Wordclouds
library(wordcloud)
Loading required package: RColorBrewer
tidy_books %>%
anti_join(stop_words) %>%
count(word) %>%
with(wordcloud(word, n, max.words = 100))
Joining, by = "word"

library(reshape2)
Attaching package: ‘reshape2’
The following object is masked from ‘package:tidyr’:
smiths
tidy_books %>%
inner_join(get_sentiments("bing")) %>%
count(word, sentiment, sort = TRUE) %>%
acast(word~sentiment, value.car = "n", fill = 0) %>%
comparison.cloud(colors = c("gray20", "gray80"),
max.words = 100)
Joining, by = "word"
Using n as value column: use value.var to override.

Looking at Units Beyond Just Words
PandP_sentences <- data_frame(text = prideprejudice) %>%
unnest_tokens(sentence, text, token = "sentences")
PandP_sentences$sentence[2]
[1] "however little known the feelings or views of such a man may be on his first entering a neighbourhood, this truth is so well fixed in the minds of the surrounding families, that he is considered the rightful property of some one or other of their daughters."
Figuring out how many chapters are in a book
austen_chapters <- austen_books() %>%
group_by(book) %>%
unnest_tokens(chapter, text , token = "regex",
pattern = "Chapter|CHAPTER [\\dIVXLC]") %>%
ungroup()
austen_chapters %>%
group_by(book) %>%
summarise(chapters = n())
bingnegative <- get_sentiments("bing") %>%
filter(sentiment == "negative")
wordcounts <- tidy_books %>%
group_by(book, chapter) %>%
summarize(words = n())
tidy_books %>%
semi_join(bingnegative) %>%
group_by(book, chapter) %>%
summarize(negativewords = n()) %>%
left_join(wordcounts, by = c("book", "chapter")) %>%
mutate(ratio = negativewords/words) %>%
filter(chapter !=0) %>%
top_n(1) %>%
ungroup()
Joining, by = "word"
Selecting by ratio
# Chapter 3
Term Frequency in Jane Austen’s Novels
book_words <- austen_books() %>%
unnest_tokens(word,text) %>%
count(book,word,sort = TRUE) %>%
ungroup()
total_words <- book_words %>%
group_by(book) %>%
summarize(total = sum(n))
book_words <- left_join(book_words, total_words)
Joining, by = "book"
book_words
Term frequency distribution in Jane Austen’s novels
ggplot(book_words, aes(n/total, fill = book)) +
geom_histogram(show.legend = FALSE) +
xlim(NA, 0.0009) +
facet_wrap(~book, ncol = 2, scales = "free_y")

NA
Zipf’s Law
Zipf’s Law: frequency that a word appears is inversely proportional to its rank.
freq_by_rank <- book_words %>%
group_by(book) %>%
mutate(rank = row_number(),
'term frequency' = n/total)
freq_by_rank
Graph of Zipf’s law for Jane Austen’s novels
freq_by_rank %>%
ggplot(aes(rank, `term frequency`, color = book)) +
geom_line(size = 1.1, alpha = 0.8, show.legend = TRUE) +
scale_x_log10() +
scale_y_log10()

rank_subset <- freq_by_rank %>%
filter(rank <500,
rank >10)
lm(log10(`term frequency`) ~ log10(rank), data = rank_subset)
Call:
lm(formula = log10(`term frequency`) ~ log10(rank), data = rank_subset)
Coefficients:
(Intercept) log10(rank)
-0.6226 -1.1125
freq_by_rank %>%
ggplot(aes(rank, `term frequency`, color = book))+
geom_abline(intercept = -0.62, slope = -1.1, color = "gray50", linetype = 2) +
geom_line(size = 1.1, alpha = 0.8, show.legend = FALSE) +
scale_x_log10() +
scale_y_log10()

The bind_tf_idf Function
book_words <- book_words %>%
bind_tf_idf(word, book, n)
book_words
book_words %>%
select(-total) %>%
arrange(desc(tf_idf))
book_words %>%
arrange(desc(tf_idf)) %>%
mutate(word = factor(word, levels = rev(unique(word)))) %>%
group_by(book) %>%
top_n(15) %>%
ungroup() %>%
ggplot(aes(word, tf_idf, fill = book)) +
geom_col(show.legend = FALSE) +
labs(x = NULL, y = "tf-idf") +
facet_wrap(~book, ncol = 2,scales = "free") +
coord_flip()
Selecting by tf_idf

A corpus of Physics Text
physics <- gutenberg_download(c(37729, 14725, 13476, 5001),
meta_fields = "author")
physics_words <- physics %>%
unnest_tokens(word, text) %>%
count(author, word, sort = TRUE) %>%
ungroup()
physics_words
author_order <- c("Galilei, Galileo", "Huygens, Christiaan", "Tesla, Nikola", "Einstein, Albert")
plot_physics <- physics_words %>%
bind_tf_idf(word, author, n) %>%
arrange(desc(tf_idf)) %>%
mutate(word = factor(word, levels = rev(unique(word)))) %>%
mutate(author = factor(author, levels = author_order))
plot_physics
plot_physics %>%
group_by(author) %>%
top_n(15, tf_idf) %>%
ungroup() %>%
mutate(word = reorder(word, tf_idf)) %>%
ggplot(aes(word, tf_idf, fill = author)) +
geom_col(show.legend = FALSE) +
labs(x = NULL, y = "tf-idf") +
facet_wrap(~author, ncol = 2, scales = "free") +
coord_flip()

NA
Isolating “eq” from Einstein’s text
physics %>%
filter(str_detect(text, "eq\\.")) %>%
select(text)
K1 was used for the coordinate system for Einstein
physics %>%
filter(str_detect(text, "K1")) %>%
select(text)
NA
physics %>%
filter(str_detect(text, "AK")) %>%
select(text)
mystopwords <- data_frame(word = c("eq","co","rc","ac","ak","bn",
"fig", "file", "cg", "cb", "cm"))
physics_words <- anti_join(physics_words, mystopwords, by = "word")
plot_physics <- physics_words %>%
bind_tf_idf(word, author, n) %>%
arrange(desc(tf_idf)) %>%
mutate(word = factor(word, levels = rev(unique(word)))) %>%
group_by(author) %>%
top_n(15, tf_idf) %>%
ungroup() %>%
mutate(author = factor(author, levels = author_order))
plot_physics
ggplot(plot_physics, aes(word, tf_idf, fill = author)) +
geom_col(show.legend = FALSE) +
labs( x = NULL, y = "tf-idf") +
facet_wrap(~author, ncol = 2, scales = "free") +
coord_flip()

Chapter 4 Relationships Between Words: N-grams and Correlations
austen_bigrams <- austen_books() %>%
unnest_tokens(bigram, text, token = "ngrams", n = 2)
austen_bigrams
Counting and Filtering N-grams
austen_bigrams %>%
count(bigram, sort = TRUE)
bigrams_separated <- austen_bigrams %>%
separate(bigram, into = c("word1", "word2"), sep = " ")
bigrams_filtered <- bigrams_separated %>%
filter(!word1 %in% stop_words$word) %>%
filter(!word2 %in% stop_words$word)
bigram_counts <- bigrams_filtered %>%
count(word1, word2, sort = TRUE)
bigram_counts
bigrams_united <- bigrams_filtered %>%
unite(bigram, word1, word2, sep = " ")
bigrams_united
austen_books() %>%
unnest_tokens(trigram, text, token = "ngrams", n = 3) %>%
separate(trigram, into = c("word1", "word2", "word3"), sep = " ") %>%
filter(!word1 %in% stop_words$word,
!word2 %in% stop_words$word,
!word3 %in% stop_words$word) %>%
count(word1, word2, word3, sort = TRUE)
Analyzing Bigrams
Identifying the “streets” in each book.
bigrams_filtered %>%
filter(word2 == "street") %>%
count(book, word1, sort = TRUE)
bigram_tf_idf <- bigrams_united %>%
count(book, bigram) %>%
bind_tf_idf(bigram, book, n) %>%
arrange(desc(tf_idf))
bigram_tf_idf
The 12 bigrams with the highest tf-idf from each Jane Austen novel.
bigram_tf_idf %>%
arrange(desc(tf_idf)) %>%
mutate(bigram = factor(bigram, levels = rev(unique(bigram)))) %>%
group_by(book) %>%
top_n(12,tf_idf) %>%
ungroup() %>%
ggplot(aes(x = bigram, y = tf_idf, fill = book))+
geom_col(show.legend = FALSE) +
facet_wrap(~book, ncol = 2, scales = "free")+
coord_flip()

Using Bigrams to Provide Context in Sentiment Analysis
bigrams_separated %>%
filter(word1 == "not") %>%
count(word1, word2, sort = TRUE)
AFINN <- get_sentiments("afinn")
AFINN
not_words <- bigrams_separated %>%
rename(word = word2) %>%
filter(word1 == "not") %>%
inner_join(AFINN) %>%
count(word, score, sort = TRUE) %>%
ungroup()
Joining, by = "word"
not_words
NA
The 20 sords followed by “not” that had the greatest contribution to sentiment scores, in either a postivie or negative direction.
not_words %>%
mutate(contribution = n * score) %>%
arrange(desc(abs(contribution))) %>%
head(20) %>%
mutate(word = reorder(word, contribution)) %>%
ggplot(aes(word, n * score, fill = n * score > 0)) +
geom_col(show.legend = FALSE) +
xlab("Words preceded by \"not\"") +
ylab("Sentiment score * number of occurences") +
coord_flip()

negation_words <- c("not", "no", "never", "without")
negated_words <- bigrams_separated %>%
filter(word1 %in% negation_words) %>%
inner_join(AFINN, by = c(word2 = "word")) %>%
count(word1, word2, sort = TRUE) %>%
ungroup()
negated_words %>%
inner_join(not_words) %>%
mutate(contribution = n * score) %>%
arrange(desc(abs(contribution))) %>%
head(12) %>%
mutate(word = reorder(word, contribution)) %>%
ggplot(aes(word, n * score, fill = n * score > 0)) +
geom_col(show.legend = FALSE) +
xlab("Words preceded by \"not\"") +
ylab("Sentiment score * number of occurences") +
coord_flip() +
facet_wrap(~word1)
Joining, by = "n"

Analyzing networks with igraph.
Create an igraph object from tidy data using the ‘graph_from_data_frame()’ function, which takes a data frame of edges with columns for “from”, “to” and edge attributes (in this case n).
library(igraph)
Attaching package: ‘igraph’
The following objects are masked from ‘package:dplyr’:
as_data_frame, groups, union
The following objects are masked from ‘package:purrr’:
compose, simplify
The following object is masked from ‘package:tidyr’:
crossing
The following object is masked from ‘package:tibble’:
as_data_frame
The following objects are masked from ‘package:stats’:
decompose, spectrum
The following object is masked from ‘package:base’:
union
bigram_counts
bigram_graph <- bigram_counts %>%
filter(n > 20) %>%
graph_from_data_frame()
bigram_graph
IGRAPH ff05f93 DN-- 91 77 --
+ attr: name (v/c), n (e/n)
+ edges from ff05f93 (vertex names):
[1] sir ->thomas miss ->crawford captain ->wentworth miss ->woodhouse frank ->churchill lady ->russell lady ->bertram
[8] sir ->walter miss ->fairfax colonel ->brandon miss ->bates lady ->catherine sir ->john jane ->fairfax
[15] miss ->tilney lady ->middleton miss ->bingley thousand ->pounds miss ->dashwood miss ->bennet john ->knightley
[22] miss ->morland captain ->benwick dear ->miss miss ->smith miss ->crawford's henry ->crawford miss ->elliot
[29] dr ->grant miss ->bertram sir ->thomas's ten ->minutes miss ->price miss ->taylor sir ->william
[36] john ->dashwood de ->bourgh dear ->sir dear ->fanny miss ->darcy mansfield->park captain ->harville
[43] charles ->hayter dear ->emma maple ->grove lady ->russell's miss ->steeles cried ->emma half ->hour
[50] harriet ->smith miss ->steele robert ->martin short ->time captain ->wentworth's colonel ->forster miss ->bertrams
+ ... omitted several edges
Commone bigrams in Pride and Prejudice, showing those that occurred more thatn 20 times and where neighter word was a stop word
library(ggraph)
set.seed(2017)
ggraph(bigram_graph, layout = "fr") +
geom_edge_link() +
geom_node_point() +
geom_node_text(aes(label = name), vjust = 1, hjust = 1)

Common bigrams in Pride and Prejudice, with some polishing
set.seed(2016)
a <- grid::arrow(type = "closed", length = unit(.15, "inches"))
ggraph(bigram_graph, layout = "fr") +
geom_edge_link(aes(edge_alpha = n), show.legend = FALSE,
arrow = a, end_cap = circle(.07, 'inches')) +
geom_node_point(color = "lightblue", size = 5) +
geom_node_text(aes(label = name), vjust = 1, hjust = 1) +
theme_void()

Topicmodels package
library(tm)
data("AssociatedPress", package = "topicmodels")
AssociatedPress
<<DocumentTermMatrix (documents: 2246, terms: 10473)>>
Non-/sparse entries: 302031/23220327
Sparsity : 99%
Maximal term length: 18
Weighting : term frequency (tf)
terms <- Terms(AssociatedPress)
head(terms)
[1] "aaron" "abandon" "abandoned" "abandoning" "abbott" "abboud"
# library(tidyverse)
# library(tidytext)
ap_td <- tidy(AssociatedPress)
ap_td
ap_sentiments <- ap_td %>%
inner_join(get_sentiments("bing"), by = c( term = "word"))
ap_sentiments
# ggplot2
ap_sentiments %>%
count(sentiment, term, wt = count) %>%
ungroup() %>%
filter(n>= 200) %>%
mutate(n = ifelse(sentiment == "negative", -n, n)) %>%
mutate(term = reorder(term, n)) %>%
ggplot(aes(term, n, fill = sentiment)) +
geom_col() +
ylab("Contribution to sentiment") +
coord_flip()

library(methods)
data("data_corpus_inaugural", package = "quanteda")
inaug_dfm <- quanteda::dfm(data_corpus_inaugural, verbose = FALSE)
inaug_dfm
Document-feature matrix of: 58 documents, 9,357 features (91.8% sparse).
inaug_td <- tidy(inaug_dfm)
inaug_td
inaug_tf_idf <- inaug_td %>%
bind_tf_idf(term, document, count) %>%
arrange(desc(tf_idf))
inaug_tf_idf
NA
inaug_tf_idf %>%
filter(document %in% c("1861-Lincoln", "1933-Roosevelt", "1961-Kennedy", "2009-Obama")) %>%
ggplot(aes(reorder(term,tf_idf), tf_idf, fill = tf_idf)) +
geom_col()+
coord_flip()+
facet_wrap(~document)

LS0tCnRpdGxlOiAiVGlkeSBUZXh0IE5vdGVib29rIgphdXRob3I6ICJTZWFuIE5ndXllbiIKc3VidGl0bGU6IEJ5IERhdmlkIFJvYmluc29uIGFuZCBKdWxpYSBTaWxnZQpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGRmX3ByaW50OiBwYWdlZAogICAgdGhlbWU6IGZsYXRseQogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQKLS0tCgpUaGlzIGlzIG15IG5vdGVib29rIHRoYXQgSSB3aWxsIHVzZWQgdG8gZ28gdGhyb3VnaCB0aGUgdGlkeSB0ZXh0IG5vdGVib29rIGJ5IEp1bGlhIFNpbGdlIGFuZCBEYXZpZCBSb2JpbnNvbi4gCgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkodGlkeXRleHQpCmxpYnJhcnkoc3RyaW5ncikKbGlicmFyeShqYW5lYXVzdGVucikKCgp0ZXh0IDwtIGMoIkJlY2F1c2UgSSBjb3VsZCBub3Qgc3RvcCBmb3IgRGVhdGggLSAiLCAKICAgICAgICAgICJIZSBraW5kbHkgc3RvcHBlZCBmb3IgbWUgLSIsCiAgICAgICAgICAiVGhlIENhcnJpYWdlIGhlbGQgYnV0IGp1c3QgT3Vyc2VsdmVzIC0iLAogICAgICAgICAgImFuZCBJbW1vcnRhbGl0eSIpCgp0ZXh0CmBgYAoKYGBge3J9CnRleHRfZGYgPC0gZGF0YV9mcmFtZShsaW5lID0gMTo0LCB0ZXh0ID0gdGV4dCkKCnRleHRfZGYKYGBgCmBgYHtyfQp0ZXh0X2RmICU+JSAKICB1bm5lc3RfdG9rZW5zKHdvcmQsIHRleHQpICAjIHRvX2xvd2VyID0gRkFMU0UgdG8ga2VlcCB1cHBlcmNhc2UKYGBgCgojIyNXb3JraW5nIHdpdGggSmFuZSBBdXN0ZW4gQm9va3MKClVzaW5nIHRoZSBKYW5lIEF1c3RlbiBkYXRhc2V0IHdlIGNhbiBtYWtlIGl0IHRpZHkuICBGaXJzdCB3ZSdsbCB1c2UgbXV0YXRlIHRvIGNyZWF0ZSBhIGNvbHVtbiBmcm9tIGV4aXN0aW5nIGRhdGEuICBBbm5vdGF0ZSBsaW5lIG51bWJlcnMgYW5kIHRoZW4ga2VlcCB0cmFjayBvZiBjaGFwdGVycyB1c2luZyByZWdleC4KCmBgYHtyfQpvcmlnaW5hbF9ib29rcyA8LSBhdXN0ZW5fYm9va3MoKSAlPiUgCiAgZ3JvdXBfYnkoYm9vaykgJT4lIAogIG11dGF0ZShsaW5lbnVtYmVyID0gcm93X251bWJlcigpLAogICAgICAgICBjaGFwdGVyID0gY3Vtc3VtKHN0cl9kZXRlY3QodGV4dCwgcmVnZXgoIl5jaGFwdGVyIFtcXGRpdnhsY10iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlnbm9yZS5jYXNlID0gVFJVRSkpKSkgJT4lIAogIHVuZ3JvdXAoKQoKb3JpZ2luYWxfYm9va3MKYGBgCgpSZXN0cnVjdHVyZSBpdCBpbiAqb25lLXRva2VuLXBlci1yb3cqIGZvcm1hdCB3aXRoIHRoZSAqKnVubmVzdF90b2tlbnMoKSoqIGZ1bmN0aW9uLgoKYGBge3J9CnRpZHlfYm9va3MgPC0gb3JpZ2luYWxfYm9va3MgJT4lIAogIHVubmVzdF90b2tlbnMod29yZCwgdGV4dCkKCnRpZHlfYm9va3MKYGBgCiAKIyMjI1JlbW92aW5nIHN0b3Agd29yZHMKV2UgY2FuIHJlbW92ZSAqc3RvcCB3b3JkcyogCgpgYGB7cn0KZGF0YShzdG9wX3dvcmRzKQoKdGlkeV9ib29rcyA8LSB0aWR5X2Jvb2tzICU+JSAKICBhbnRpX2pvaW4oc3RvcF93b3JkcykKCnRpZHlfYm9va3MgJT4lIAogIGNvdW50KHdvcmQsIHNvcnQgPSBUUlVFKQoKCmBgYAoKUGxvdHRpbmcgZGF0YSAKCmBgYHtyfQp0aWR5X2Jvb2tzICU+JSAKICBjb3VudCh3b3JkLCBzb3J0ID0gVFJVRSkgJT4lIAogIGZpbHRlcihuID4gNjAwKSAlPiUgCiAgbXV0YXRlKHdvcmQgPSByZW9yZGVyKHdvcmQsIG4pKSAlPiUgCiAgZ2dwbG90KGFlcyh3b3JkLCBuKSkrCiAgZ2VvbV9jb2woKSsKICB4bGFiKE5VTEwpKwogIGNvb3JkX2ZsaXAoKSsKICB0aGVtZV9idygpCmBgYAojIyMjIEd1dGVuYmVyZ3IKCkhHIHdlbGxzIGJvb2tzCmBgYHtyfQpsaWJyYXJ5KGd1dGVuYmVyZ3IpCgoKaGd3ZWxscyA8LSAgZ3V0ZW5iZXJnX2Rvd25sb2FkKGMoMzUsIDM2LCA1MjMwLCAxNTkpKQoKdGlkeV9oZ3dlbGxzIDwtIGhnd2VsbHMgJT4lIAogIHVubmVzdF90b2tlbnMod29yZCwgdGV4dCkgJT4lIAogIGFudGlfam9pbihzdG9wX3dvcmRzKQoKdGlkeV9oZ3dlbGxzICU+JSAKICBjb3VudCh3b3JkLCBzb3J0ID0gVFJVRSkKYGBgCgpNb3N0IGNvbW1vbmUgd29yZHMgaW4gbm92ZWxzIGJ5IEJyb250ZSBzaXN0ZXJzCgpgYGB7cn0KYnJvbnRlIDwtICBndXRlbmJlcmdfZG93bmxvYWQoYygxMjYwLCA3NjgsIDk2OSwgOTE4MiwgNzY3KSkKCnRpZHlfYnJvbnRlIDwtIGJyb250ZSAlPiUgCiAgdW5uZXN0X3Rva2Vucyh3b3JkLCB0ZXh0KSAlPiUgCiAgYW50aV9qb2luKHN0b3Bfd29yZHMpCgp0aWR5X2Jyb250ZSAlPiUgCiAgY291bnQod29yZCwgc29ydCA9IFRSVUUpCgpgYGAKCkNvbXBhcmluZyBCcm9udGUgYW5kIEhHIFdlbGxzCgpgYGB7cn0KZnJlcXVlbmN5IDwtIGJpbmRfcm93cyhtdXRhdGUodGlkeV9icm9udGUsIGF1dGhvciA9ICJCcm9udGUgU2lzdGVycyIpLAogICAgICAgICAgICAgICAgICAgICAgIG11dGF0ZSh0aWR5X2hnd2VsbHMsIGF1dGhvciA9ICJILkcuIFdlbGxzIiksCiAgICAgICAgICAgICAgICAgICAgICAgbXV0YXRlKHRpZHlfYm9va3MsIGF1dGhvciA9ICJKYW5lIEF1c3RlbiIpKSAlPiUgCiAgbXV0YXRlKHdvcmQgPSBzdHJfZXh0cmFjdCh3b3JkLCAiW2EteiddKyIpKSAlPiUgCiAgY291bnQoYXV0aG9yLCB3b3JkKSAlPiUgCiAgZ3JvdXBfYnkoYXV0aG9yKSAlPiUgCiAgbXV0YXRlKHByb3BvcnRpb24gPSBuIC8gc3VtKG4pKSAlPiUgCiAgc2VsZWN0KC1uKSAlPiUgCiAgc3ByZWFkKGF1dGhvciwgcHJvcG9ydGlvbikgJT4lIAogIGdhdGhlcihhdXRob3IsIHByb3BvcnRpb24sIGBCcm9udGUgU2lzdGVyc2A6YEguRy4gV2VsbHNgKQpgYGAKCiMjI0dyYXBoaW5nIHRoZSBjb21wYXJpc29ucwoKYGBge3J9CmxpYnJhcnkoc2NhbGVzKQoKZ2dwbG90KGZyZXF1ZW5jeSwgYWVzKHggPSBwcm9wb3J0aW9uLCB5ID0gYEphbmUgQXVzdGVuYCwKICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gYWJzKGBKYW5lIEF1c3RlbmAgLSBwcm9wb3J0aW9uKSkpICsKICBnZW9tX2FibGluZShjb2xvciA9ICJncmF5NDAiLCBsdHkgPSAyKSArCiAgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAwLjEsIHNpemUgPSAyLjUsIHdpZHRoID0gMC4zLCBoZWlnaHQgPSAwLjMpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gd29yZCwgY2hlY2tfb3ZlcmxhcCA9IFRSVUUsIHZqdXN0ID0gMS41KSkrCiAgc2NhbGVfeF9sb2cxMChsYWJlbHMgPSBwZXJjZW50X2Zvcm1hdCgpKSArCiAgc2NhbGVfeV9sb2cxMChsYWJlbHMgPSBwZXJjZW50X2Zvcm1hdCgpKSArCiAgc2NhbGVfY29sb3JfZ3JhZGllbnQobGltaXRzID0gYygwLDAuMDAxKSwKICAgICAgICAgICAgICAgICAgICAgICBsb3cgPSAiZGFya3NsYXRlZ3JheTQiLCBoaWdoID0gImdyYXk3NSIpICsKICBmYWNldF93cmFwKH5hdXRob3IsIG5jb2wgPSAyKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgbGFicyh5ID0gIkphbmUgQXVzdGVuIiwgeCA9IE5VTEwpCiAgCmBgYAoKIyMjQ29ycmVsYXRpb24gYW5hbHlzaXMKYGBge3J9CmNvci50ZXN0KGRhdGEgPSBmcmVxdWVuY3lbZnJlcXVlbmN5JGF1dGhvciA9PSAiQnJvbnRlIFNpc3RlcnMiLF0sCiAgICAgICAgIH4gcHJvcG9ydGlvbiArIGBKYW5lIEF1c3RlbmApCmBgYAoKYGBge3J9CmNvci50ZXN0KGRhdGEgPSBmcmVxdWVuY3lbZnJlcXVlbmN5JGF1dGhvciA9PSAiSC5HLiBXZWxscyIsXSwKICAgICAgICAgfiBwcm9wb3J0aW9uICsgYEphbmUgQXVzdGVuYCkKYGBgCgojIFNlbnRpbWVudHMgZGF0YXNldAoKYGBge3J9CmxpYnJhcnkodGlkeXRleHQpCgpzZW50aW1lbnRzCmBgYApTZW50aW1lbnQgc2NvcmVzCmBgYHtyfQojIEFGSU5OCmdldF9zZW50aW1lbnRzKCJhZmlubiIpCiMgQmluZwpnZXRfc2VudGltZW50cygiYmluZyIpCiMgTlJDCmdldF9zZW50aW1lbnRzKCJucmMiKQpgYGAKIFNlbnRpbWVudCBhbmFseXNpcyBjYW4gYmUgcGVyZm9ybWVkIHdpdGggYW4gKippbm5lciBqb2luKioKIAogCmBgYHtyfQp0aWR5X2Jvb2tzIDwtIGF1c3Rlbl9ib29rcygpICU+JSAKICBncm91cF9ieShib29rKSAlPiUgCiAgbXV0YXRlKGxpbmVudW1iZXIgPSByb3dfbnVtYmVyKCksCiAgICAgICAgIGNoYXB0ZXIgPSBjdW1zdW0oc3RyX2RldGVjdCh0ZXh0LCByZWdleCgiXmNoYXB0ZXIgW1xcZGl2eGxjXSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZ25vcmVfY2FzZSA9IFRSVUUpKSkpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIHVubmVzdF90b2tlbnMod29yZCwgdGV4dCkKCnRpZHlfYm9va3MKYGBgCgpHZXQgYSBsaXN0IG9mIHdvcmRzIHRoYXQgYXJlIGFzc29jaWF0ZWQgd2l0aCAnam95JyBhbmQgcGVyZm9ybSBhbiBpbm5lcl9qb2luIHRvIGZpbmQgd29yZHMgdGhhdCBhcmUgaGF2ZSAnam95JyBzZW50aW1lbnQgd2l0aGluIEphbmUgQXVzdGVuJ3MgRW1tYSBib29rLgpgYGB7cn0KCiMgRmlsdGVyIHdvcmRzIGFzc29jaWF0ZWQgd2l0aCAnam95JwpucmNqb3kgPC0gZ2V0X3NlbnRpbWVudHMoIm5yYyIpICU+JSAKICBmaWx0ZXIoc2VudGltZW50ID09ICJqb3kiKQoKIyBGaW5kIHdvcmRzIGFzc29jaWF0ZWQgd2l0aCBqb3kgaW4gRW1tYQp0aWR5X2Jvb2tzICU+JSAKICBmaWx0ZXIoYm9vayA9PSAiRW1tYSIpICU+JSAKICBpbm5lcl9qb2luKG5yY2pveSkgJT4lIAogIGNvdW50KHdvcmQsIHNvcnQgPSBUUlVFKQpgYGAKQ291bnQgdXAgcG9zaXRpdmUgb3IgbmVnYXRpdmUgd29yZHMgdGhhdCBhcmUgd2l0aGluIHNlY3Rpb25zIG9mIGVhY2ggYm9vay4gIFdlIHdpbGwgZGVmaW5lIGFuIGluZGV4IHRvIGtlZXAgdHJhY2sgb2Ygd2hlcmUgd2UgYXJlIGluIHRoZSBuYXJyYXRpdmUuICBUaGUgaW5kZXggd2lsbCBjb3VudCB1cCBzZWN0aW9ucyBvZiA4MCBsaW5lcyBvZiB0ZXh0CmBgYHtyfQpqYW5lYXVzdGVuc2VudGltZW50IDwtIHRpZHlfYm9va3MgJT4lIAogIGlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoImJpbmciKSkgJT4lIAogIGNvdW50KGJvb2ssIGluZGV4ID0gbGluZW51bWJlciAlLyUgODAsIHNlbnRpbWVudCkgJT4lIAogIHNwcmVhZChzZW50aW1lbnQsIG4sIGZpbGwgPTApICU+JSAKICBtdXRhdGUoc2VudGltZW50ID0gcG9zaXRpdmUgLSBuZWdhdGl2ZSkKCmphbmVhdXN0ZW5zZW50aW1lbnQKYGBgCmBgYHtyfQpnZ3Bsb3QoamFuZWF1c3RlbnNlbnRpbWVudCwgYWVzKGluZGV4LCBzZW50aW1lbnQsIGZpbGwgPSBib29rKSkgKwogIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpICsKICBmYWNldF93cmFwKH5ib29rLCBuY29sID0gMiwgc2NhbGVzID0gImZyZWVfeCIpCmBgYAoKYGBge3J9CnByaWRlX3ByZWp1ZGljZSA8LSB0aWR5X2Jvb2tzICU+JSAKICBmaWx0ZXIoYm9vayA9PSAiUHJpZGUgJiBQcmVqdWRpY2UiKQoKcHJpZGVfcHJlanVkaWNlCmBgYApgYGB7cn0KYWZpbm4gPC0gcHJpZGVfcHJlanVkaWNlICU+JSAKICBpbm5lcl9qb2luKGdldF9zZW50aW1lbnRzKCJhZmlubiIpKSAlPiUgCiAgZ3JvdXBfYnkoaW5kZXggPSBsaW5lbnVtYmVyICUvJSA4MCkgJT4lIAogIHN1bW1hcmlzZShzZW50aW1lbnQgPSBzdW0oc2NvcmUpKSAlPiUgCiAgbXV0YXRlKG1ldGhvZCA9ICJBRklOTiIpCgphZmlubgoKYGBgCmBgYHtyfQpiaW5nX2FuZF9ucmMgPC0gYmluZF9yb3dzKAogIHByaWRlX3ByZWp1ZGljZSAlPiUgCiAgICBpbm5lcl9qb2luKGdldF9zZW50aW1lbnRzKCJiaW5nIikpICU+JSAKICAgIG11dGF0ZShtZXRob2QgPSAiQmluZyBldCBhbC4iKSwKICBwcmlkZV9wcmVqdWRpY2UgJT4lIAogICAgaW5uZXJfam9pbihnZXRfc2VudGltZW50cygibnJjIikgJT4lIAogICAgICAgICAgICAgICAgIGZpbHRlcihzZW50aW1lbnQgJWluJSBjKCJwb3NpdGl2ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5lZ2F0aXZlIikpKSAlPiUgCiAgICBtdXRhdGUobWV0aG9kID0gIk5SQyIpKSAlPiUgCiAgICBjb3VudChtZXRob2QsIGluZGV4ID0gbGluZW51bWJlciAlLyUgODAsIHNlbnRpbWVudCkgJT4lIAogICAgc3ByZWFkKHNlbnRpbWVudCwgbiwgZmlsbCA9IDApICU+JSAKICAgIG11dGF0ZShzZW50aW1lbnQgPSBwb3NpdGl2ZSAtIG5lZ2F0aXZlKQoKYmluZ19hbmRfbnJjCmBgYApDb21wYXJpbmcgdGhyZWUgc2VudGltZW50IGxleGljb25zIHVzaW5nIFByaWRlIGFuZCBQcmVqdWRpY2UKYGBge3J9CmJpbmRfcm93cyhhZmlubiwKICAgICAgICAgIGJpbmdfYW5kX25yYykgJT4lIAogIGdncGxvdChhZXMoaW5kZXgsIHNlbnRpbWVudCwgZmlsbCA9IG1ldGhvZCkpICsKICAgIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpICsKICAgIGZhY2V0X3dyYXAofm1ldGhvZCwgbmNvbCA9IDEgLCBzY2FsZXMgPSAiZnJlZV95IikKYGBgCmBgYHtyfQpnZXRfc2VudGltZW50cygibnJjIikgJT4lIAogIGZpbHRlcihzZW50aW1lbnQgJWluJSBjKCJwb3NpdGl2ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIm5lZ2F0aXZlIikpICU+JSAKICBjb3VudChzZW50aW1lbnQpCgoKZ2V0X3NlbnRpbWVudHMoImJpbmciKSAlPiUgCiAgY291bnQoc2VudGltZW50KQpgYGAKYGBge3J9CmJpbmdfd29yZHNfY291bnRzIDwtIHRpZHlfYm9va3MgJT4lIAogIGlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoImJpbmciKSkgJT4lIAogIGNvdW50KHdvcmQsIHNlbnRpbWVudCwgc29ydCA9IFRSVUUpICU+JSAKICB1bmdyb3VwKCkKCmJpbmdfd29yZHNfY291bnRzCmBgYApgYGB7cn0KYmluZ193b3Jkc19jb3VudHMgJT4lIAogIGdyb3VwX2J5KHNlbnRpbWVudCkgJT4lIAogIHRvcF9uKDEwKSAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBtdXRhdGUod29yZCA9IHJlb3JkZXIod29yZCwgbikpICU+JSAKICBnZ3Bsb3QoYWVzKHdvcmQsIG4sIGZpbGwgPSBzZW50aW1lbnQpKSArCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkrCiAgZmFjZXRfd3JhcCh+c2VudGltZW50LCBzY2FsZXMgPSAiZnJlZV95IikgKwogIGxhYnMoeSA9ICJDb250cmlidXRpb24gdG8gc2VudGltZW50IiwKICAgICAgIHggPSBOVUxMLAogICAgICAgdGl0bGUgPSAiVG9wIDEwIHdvcmRzIGJ5IFNlbnRpbWVudCBmb3IgUHJpZGUgYW5kIFByZWp1ZGljZSIpICsKICBjb29yZF9mbGlwKCkKYGBgCk1pc3MgaXMgcmVmZXJyZWQgdG8gYXMgYSBuZWdhdGl2ZSB3b3JkIGJ1dCBpcyBub3QgaW4gSmFuZSBBdXN0ZW4ncyB3b3Jrcy4KYGBge3J9CmN1c3RvbV9zdG9wX3dvcmRzIDwtIGJpbmRfcm93cyhkYXRhX2ZyYW1lKHdvcmQgPSBjKCJtaXNzIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxleGljb24gPSBjKCJjdXN0b20iKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdG9wX3dvcmRzKQoKY3VzdG9tX3N0b3Bfd29yZHMKYGBgCiMgV29yZGNsb3VkcwoKYGBge3J9CmxpYnJhcnkod29yZGNsb3VkKQoKdGlkeV9ib29rcyAlPiUgCiAgYW50aV9qb2luKHN0b3Bfd29yZHMpICU+JSAKICBjb3VudCh3b3JkKSAlPiUgCiAgd2l0aCh3b3JkY2xvdWQod29yZCwgbiwgbWF4LndvcmRzID0gMTAwKSkKYGBgCgpgYGB7cn0KbGlicmFyeShyZXNoYXBlMikKCnRpZHlfYm9va3MgJT4lIAogIGlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoImJpbmciKSkgJT4lIAogIGNvdW50KHdvcmQsIHNlbnRpbWVudCwgc29ydCA9IFRSVUUpICU+JSAKICBhY2FzdCh3b3JkfnNlbnRpbWVudCwgdmFsdWUuY2FyID0gIm4iLCBmaWxsID0gMCkgJT4lIAogIGNvbXBhcmlzb24uY2xvdWQoY29sb3JzID0gYygiZ3JheTIwIiwgImdyYXk4MCIpLAogICAgICAgICAgICAgICAgICAgbWF4LndvcmRzID0gMTAwKQpgYGAKCkxvb2tpbmcgYXQgVW5pdHMgQmV5b25kIEp1c3QgV29yZHMKYGBge3J9ClBhbmRQX3NlbnRlbmNlcyA8LSAgZGF0YV9mcmFtZSh0ZXh0ID0gcHJpZGVwcmVqdWRpY2UpICU+JSAKICB1bm5lc3RfdG9rZW5zKHNlbnRlbmNlLCB0ZXh0LCB0b2tlbiA9ICJzZW50ZW5jZXMiKQpgYGAKCmBgYHtyfQpQYW5kUF9zZW50ZW5jZXMkc2VudGVuY2VbMl0KYGBgCkZpZ3VyaW5nIG91dCBob3cgbWFueSBjaGFwdGVycyBhcmUgaW4gYSBib29rCmBgYHtyfQphdXN0ZW5fY2hhcHRlcnMgPC0gYXVzdGVuX2Jvb2tzKCkgJT4lIAogIGdyb3VwX2J5KGJvb2spICU+JSAKICB1bm5lc3RfdG9rZW5zKGNoYXB0ZXIsIHRleHQgLCB0b2tlbiA9ICJyZWdleCIsCiAgICAgICAgICAgICAgICBwYXR0ZXJuID0gIkNoYXB0ZXJ8Q0hBUFRFUiBbXFxkSVZYTENdIikgJT4lIAogIHVuZ3JvdXAoKQoKCmF1c3Rlbl9jaGFwdGVycyAlPiUgCiAgZ3JvdXBfYnkoYm9vaykgJT4lIAogIHN1bW1hcmlzZShjaGFwdGVycyA9IG4oKSkKYGBgCgoKYGBge3J9CmJpbmduZWdhdGl2ZSA8LSBnZXRfc2VudGltZW50cygiYmluZyIpICU+JSAKICBmaWx0ZXIoc2VudGltZW50ID09ICJuZWdhdGl2ZSIpCgp3b3JkY291bnRzIDwtIHRpZHlfYm9va3MgJT4lIAogIGdyb3VwX2J5KGJvb2ssIGNoYXB0ZXIpICU+JSAKICBzdW1tYXJpemUod29yZHMgPSBuKCkpCgp0aWR5X2Jvb2tzICU+JSAKICBzZW1pX2pvaW4oYmluZ25lZ2F0aXZlKSAlPiUgCiAgZ3JvdXBfYnkoYm9vaywgY2hhcHRlcikgJT4lIAogIHN1bW1hcml6ZShuZWdhdGl2ZXdvcmRzID0gbigpKSAlPiUgCiAgbGVmdF9qb2luKHdvcmRjb3VudHMsIGJ5ID0gYygiYm9vayIsICJjaGFwdGVyIikpICU+JSAKICBtdXRhdGUocmF0aW8gPSBuZWdhdGl2ZXdvcmRzL3dvcmRzKSAlPiUgCiAgZmlsdGVyKGNoYXB0ZXIgIT0wKSAlPiUgCiAgdG9wX24oMSkgJT4lIAogIHVuZ3JvdXAoKQpgYGAKIAogIyBDaGFwdGVyIDMKIAogVGVybSBGcmVxdWVuY3kgaW4gSmFuZSBBdXN0ZW4ncyBOb3ZlbHMKIApgYGB7cn0KYm9va193b3JkcyA8LSBhdXN0ZW5fYm9va3MoKSAlPiUgCiAgdW5uZXN0X3Rva2Vucyh3b3JkLHRleHQpICU+JSAKICBjb3VudChib29rLHdvcmQsc29ydCA9IFRSVUUpICU+JSAKICB1bmdyb3VwKCkKCnRvdGFsX3dvcmRzIDwtIGJvb2tfd29yZHMgJT4lIAogIGdyb3VwX2J5KGJvb2spICU+JSAKICBzdW1tYXJpemUodG90YWwgPSBzdW0obikpCgpib29rX3dvcmRzIDwtIGxlZnRfam9pbihib29rX3dvcmRzLCB0b3RhbF93b3JkcykKCmJvb2tfd29yZHMKYGBgCgpUZXJtIGZyZXF1ZW5jeSBkaXN0cmlidXRpb24gaW4gSmFuZSBBdXN0ZW4ncyBub3ZlbHMKYGBge3J9CmdncGxvdChib29rX3dvcmRzLCBhZXMobi90b3RhbCwgZmlsbCA9IGJvb2spKSArCiAgZ2VvbV9oaXN0b2dyYW0oc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIHhsaW0oTkEsIDAuMDAwOSkgKwogIGZhY2V0X3dyYXAofmJvb2ssIG5jb2wgPSAyLCBzY2FsZXMgPSAiZnJlZV95IikKICAKYGBgCgpaaXBmJ3MgTGF3CgpaaXBmJ3MgTGF3OiBmcmVxdWVuY3kgdGhhdCBhIHdvcmQgYXBwZWFycyBpcyBpbnZlcnNlbHkgcHJvcG9ydGlvbmFsIHRvIGl0cyByYW5rLgoKYGBge3J9CmZyZXFfYnlfcmFuayA8LSBib29rX3dvcmRzICU+JSAKICBncm91cF9ieShib29rKSAlPiUgCiAgbXV0YXRlKHJhbmsgPSByb3dfbnVtYmVyKCksCiAgICAgICAgICd0ZXJtIGZyZXF1ZW5jeScgPSBuL3RvdGFsKQoKZnJlcV9ieV9yYW5rCmBgYAoKR3JhcGggb2YgWmlwZidzIGxhdyBmb3IgSmFuZSBBdXN0ZW4ncyBub3ZlbHMKYGBge3J9CgpmcmVxX2J5X3JhbmsgJT4lIAogIGdncGxvdChhZXMocmFuaywgYHRlcm0gZnJlcXVlbmN5YCwgY29sb3IgPSBib29rKSkgKwogIGdlb21fbGluZShzaXplID0gMS4xLCBhbHBoYSA9IDAuOCwgc2hvdy5sZWdlbmQgPSBUUlVFKSArCiAgc2NhbGVfeF9sb2cxMCgpICsKICBzY2FsZV95X2xvZzEwKCkKYGBgCgpgYGB7cn0KcmFua19zdWJzZXQgPC0gIGZyZXFfYnlfcmFuayAlPiUgCiAgZmlsdGVyKHJhbmsgPDUwMCwKICAgICAgICAgcmFuayA+MTApCgoKbG0obG9nMTAoYHRlcm0gZnJlcXVlbmN5YCkgfiBsb2cxMChyYW5rKSwgZGF0YSA9IHJhbmtfc3Vic2V0KQpgYGAKYGBge3J9CmZyZXFfYnlfcmFuayAlPiUgCiAgZ2dwbG90KGFlcyhyYW5rLCBgdGVybSBmcmVxdWVuY3lgLCBjb2xvciA9IGJvb2spKSsKICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSAtMC42Miwgc2xvcGUgPSAtMS4xLCBjb2xvciA9ICJncmF5NTAiLCBsaW5ldHlwZSA9IDIpICsKICBnZW9tX2xpbmUoc2l6ZSA9IDEuMSwgYWxwaGEgPSAwLjgsIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBzY2FsZV94X2xvZzEwKCkgKwogIHNjYWxlX3lfbG9nMTAoKQoKYGBgCgpUaGUgYmluZF90Zl9pZGYgRnVuY3Rpb24KCmBgYHtyfQpib29rX3dvcmRzIDwtIGJvb2tfd29yZHMgJT4lIAogIGJpbmRfdGZfaWRmKHdvcmQsIGJvb2ssIG4pCgpib29rX3dvcmRzCmBgYAoKYGBge3J9CmJvb2tfd29yZHMgJT4lIAogIHNlbGVjdCgtdG90YWwpICU+JSAKICBhcnJhbmdlKGRlc2ModGZfaWRmKSkKYGBgCgpgYGB7cn0KYm9va193b3JkcyAlPiUgCiAgYXJyYW5nZShkZXNjKHRmX2lkZikpICU+JSAKICBtdXRhdGUod29yZCA9IGZhY3Rvcih3b3JkLCBsZXZlbHMgPSByZXYodW5pcXVlKHdvcmQpKSkpICU+JSAKICBncm91cF9ieShib29rKSAlPiUgCiAgdG9wX24oMTUpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIGdncGxvdChhZXMod29yZCwgdGZfaWRmLCBmaWxsID0gYm9vaykpICsKICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgbGFicyh4ID0gTlVMTCwgeSA9ICJ0Zi1pZGYiKSArCiAgZmFjZXRfd3JhcCh+Ym9vaywgbmNvbCA9IDIsc2NhbGVzID0gImZyZWUiKSArCiAgY29vcmRfZmxpcCgpCmBgYApBIGNvcnB1cyBvZiBQaHlzaWNzIFRleHQKCmBgYHtyfQpwaHlzaWNzIDwtIGd1dGVuYmVyZ19kb3dubG9hZChjKDM3NzI5LCAxNDcyNSwgMTM0NzYsIDUwMDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRhX2ZpZWxkcyA9ICJhdXRob3IiKQpgYGAKCmBgYHtyfQpwaHlzaWNzX3dvcmRzIDwtIHBoeXNpY3MgJT4lIAogIHVubmVzdF90b2tlbnMod29yZCwgdGV4dCkgJT4lIAogIGNvdW50KGF1dGhvciwgd29yZCwgc29ydCA9IFRSVUUpICU+JSAKICB1bmdyb3VwKCkKCnBoeXNpY3Nfd29yZHMKYGBgCgpgYGB7cn0KYXV0aG9yX29yZGVyIDwtIGMoIkdhbGlsZWksIEdhbGlsZW8iLCAiSHV5Z2VucywgQ2hyaXN0aWFhbiIsICJUZXNsYSwgTmlrb2xhIiwgIkVpbnN0ZWluLCBBbGJlcnQiKQoKcGxvdF9waHlzaWNzIDwtIHBoeXNpY3Nfd29yZHMgJT4lIAogIGJpbmRfdGZfaWRmKHdvcmQsIGF1dGhvciwgbikgJT4lIAogIGFycmFuZ2UoZGVzYyh0Zl9pZGYpKSAlPiUgCiAgbXV0YXRlKHdvcmQgPSBmYWN0b3Iod29yZCwgbGV2ZWxzID0gcmV2KHVuaXF1ZSh3b3JkKSkpKSAlPiUgCiAgbXV0YXRlKGF1dGhvciA9IGZhY3RvcihhdXRob3IsIGxldmVscyA9IGF1dGhvcl9vcmRlcikpCgpwbG90X3BoeXNpY3MKYGBgCgpgYGB7cn0KcGxvdF9waHlzaWNzICU+JSAKICBncm91cF9ieShhdXRob3IpICU+JQogIHRvcF9uKDE1LCB0Zl9pZGYpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIG11dGF0ZSh3b3JkID0gcmVvcmRlcih3b3JkLCB0Zl9pZGYpKSAlPiUgCiAgZ2dwbG90KGFlcyh3b3JkLCB0Zl9pZGYsIGZpbGwgPSBhdXRob3IpKSArCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIGxhYnMoeCA9IE5VTEwsIHkgPSAidGYtaWRmIikgKwogIGZhY2V0X3dyYXAofmF1dGhvciwgbmNvbCA9IDIsIHNjYWxlcyA9ICJmcmVlIikgKwogIGNvb3JkX2ZsaXAoKQogIAoKYGBgCgpJc29sYXRpbmcgImVxIiBmcm9tIEVpbnN0ZWluJ3MgdGV4dAoKYGBge3J9CnBoeXNpY3MgJT4lIAogIGZpbHRlcihzdHJfZGV0ZWN0KHRleHQsICJlcVxcLiIpKSAlPiUgCiAgc2VsZWN0KHRleHQpCmBgYApLMSB3YXMgdXNlZCBmb3IgdGhlIGNvb3JkaW5hdGUgc3lzdGVtIGZvciBFaW5zdGVpbgpgYGB7cn0KCnBoeXNpY3MgJT4lCiAgZmlsdGVyKHN0cl9kZXRlY3QodGV4dCwgIksxIikpICU+JSAKICBzZWxlY3QodGV4dCkKCiAgCmBgYApgYGB7cn0KcGh5c2ljcyAlPiUKICBmaWx0ZXIoc3RyX2RldGVjdCh0ZXh0LCAiQUsiKSkgJT4lIAogIHNlbGVjdCh0ZXh0KQpgYGAKCmBgYHtyfQpteXN0b3B3b3JkcyA8LSBkYXRhX2ZyYW1lKHdvcmQgPSBjKCJlcSIsImNvIiwicmMiLCJhYyIsImFrIiwiYm4iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJmaWciLCAiZmlsZSIsICJjZyIsICJjYiIsICJjbSIpKQoKcGh5c2ljc193b3JkcyA8LSBhbnRpX2pvaW4ocGh5c2ljc193b3JkcywgbXlzdG9wd29yZHMsIGJ5ID0gIndvcmQiKQoKcGxvdF9waHlzaWNzIDwtIHBoeXNpY3Nfd29yZHMgJT4lIAogIGJpbmRfdGZfaWRmKHdvcmQsIGF1dGhvciwgbikgJT4lIAogIGFycmFuZ2UoZGVzYyh0Zl9pZGYpKSAlPiUgCiAgbXV0YXRlKHdvcmQgPSBmYWN0b3Iod29yZCwgbGV2ZWxzID0gcmV2KHVuaXF1ZSh3b3JkKSkpKSAlPiUgCiAgZ3JvdXBfYnkoYXV0aG9yKSAlPiUgCiAgdG9wX24oMTUsIHRmX2lkZikgJT4lIAogIHVuZ3JvdXAoKSAlPiUgCiAgbXV0YXRlKGF1dGhvciA9IGZhY3RvcihhdXRob3IsIGxldmVscyA9IGF1dGhvcl9vcmRlcikpCgpwbG90X3BoeXNpY3MKYGBgCgpgYGB7cn0KZ2dwbG90KHBsb3RfcGh5c2ljcywgYWVzKHdvcmQsIHRmX2lkZiwgZmlsbCA9IGF1dGhvcikpICsKICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgbGFicyggeCA9IE5VTEwsIHkgPSAidGYtaWRmIikgKwogIGZhY2V0X3dyYXAofmF1dGhvciwgbmNvbCA9IDIsIHNjYWxlcyA9ICJmcmVlIikgKwogIGNvb3JkX2ZsaXAoKQoKYGBgCgojIENoYXB0ZXIgNCBSZWxhdGlvbnNoaXBzIEJldHdlZW4gV29yZHM6IE4tZ3JhbXMgYW5kIENvcnJlbGF0aW9ucwoKYGBge3J9CmF1c3Rlbl9iaWdyYW1zIDwtIGF1c3Rlbl9ib29rcygpICU+JSAKICB1bm5lc3RfdG9rZW5zKGJpZ3JhbSwgdGV4dCwgdG9rZW4gPSAibmdyYW1zIiwgbiA9IDIpCgphdXN0ZW5fYmlncmFtcwoKYGBgCkNvdW50aW5nIGFuZCBGaWx0ZXJpbmcgTi1ncmFtcwpgYGB7cn0KYXVzdGVuX2JpZ3JhbXMgJT4lIAogIGNvdW50KGJpZ3JhbSwgc29ydCA9IFRSVUUpCmBgYAoKYGBge3J9CmJpZ3JhbXNfc2VwYXJhdGVkIDwtICBhdXN0ZW5fYmlncmFtcyAlPiUgCiAgc2VwYXJhdGUoYmlncmFtLCBpbnRvID0gYygid29yZDEiLCAid29yZDIiKSwgc2VwID0gIiAiKQogIApiaWdyYW1zX2ZpbHRlcmVkIDwtIGJpZ3JhbXNfc2VwYXJhdGVkICU+JSAKICBmaWx0ZXIoIXdvcmQxICVpbiUgc3RvcF93b3JkcyR3b3JkKSAlPiUgCiAgZmlsdGVyKCF3b3JkMiAlaW4lIHN0b3Bfd29yZHMkd29yZCkKCgpiaWdyYW1fY291bnRzIDwtIGJpZ3JhbXNfZmlsdGVyZWQgJT4lIAogIGNvdW50KHdvcmQxLCB3b3JkMiwgc29ydCA9IFRSVUUpCgpiaWdyYW1fY291bnRzCmBgYAoKYGBge3J9CmJpZ3JhbXNfdW5pdGVkIDwtIGJpZ3JhbXNfZmlsdGVyZWQgJT4lIAogIHVuaXRlKGJpZ3JhbSwgd29yZDEsIHdvcmQyLCBzZXAgPSAiICIpCgpiaWdyYW1zX3VuaXRlZApgYGAKCmBgYHtyfQoKYXVzdGVuX2Jvb2tzKCkgJT4lIAogIHVubmVzdF90b2tlbnModHJpZ3JhbSwgdGV4dCwgdG9rZW4gPSAibmdyYW1zIiwgbiA9IDMpICU+JSAKICBzZXBhcmF0ZSh0cmlncmFtLCBpbnRvID0gYygid29yZDEiLCAid29yZDIiLCAid29yZDMiKSwgc2VwID0gIiAiKSAlPiUgCiAgZmlsdGVyKCF3b3JkMSAlaW4lIHN0b3Bfd29yZHMkd29yZCwKICAgICAgICAgIXdvcmQyICVpbiUgc3RvcF93b3JkcyR3b3JkLAogICAgICAgICAhd29yZDMgJWluJSBzdG9wX3dvcmRzJHdvcmQpICU+JSAKICBjb3VudCh3b3JkMSwgd29yZDIsIHdvcmQzLCBzb3J0ID0gVFJVRSkKYGBgCgojIyNBbmFseXppbmcgQmlncmFtcwoKSWRlbnRpZnlpbmcgdGhlICJzdHJlZXRzIiBpbiBlYWNoIGJvb2suCgpgYGB7cn0KYmlncmFtc19maWx0ZXJlZCAlPiUgCiAgZmlsdGVyKHdvcmQyID09ICJzdHJlZXQiKSAlPiUgCiAgY291bnQoYm9vaywgd29yZDEsIHNvcnQgPSBUUlVFKQpgYGAKCmBgYHtyfQpiaWdyYW1fdGZfaWRmIDwtIGJpZ3JhbXNfdW5pdGVkICU+JSAKICBjb3VudChib29rLCBiaWdyYW0pICU+JSAKICBiaW5kX3RmX2lkZihiaWdyYW0sIGJvb2ssIG4pICU+JSAKICBhcnJhbmdlKGRlc2ModGZfaWRmKSkKCmJpZ3JhbV90Zl9pZGYgCmBgYAoKVGhlIDEyIGJpZ3JhbXMgd2l0aCB0aGUgaGlnaGVzdCB0Zi1pZGYgZnJvbSBlYWNoIEphbmUgQXVzdGVuIG5vdmVsLgpgYGB7cn0KYmlncmFtX3RmX2lkZiAlPiUgCiAgYXJyYW5nZShkZXNjKHRmX2lkZikpICU+JSAKICBtdXRhdGUoYmlncmFtID0gZmFjdG9yKGJpZ3JhbSwgbGV2ZWxzID0gcmV2KHVuaXF1ZShiaWdyYW0pKSkpICU+JSAKICBncm91cF9ieShib29rKSAlPiUgCiAgdG9wX24oMTIsdGZfaWRmKSAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBnZ3Bsb3QoYWVzKHggPSBiaWdyYW0sIHkgPSB0Zl9pZGYsIGZpbGwgPSBib29rKSkrCiAgICBnZW9tX2NvbChzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgICBmYWNldF93cmFwKH5ib29rLCBuY29sID0gMiwgc2NhbGVzID0gImZyZWUiKSsKICAgIGNvb3JkX2ZsaXAoKQoKYGBgClVzaW5nIEJpZ3JhbXMgdG8gUHJvdmlkZSBDb250ZXh0IGluIFNlbnRpbWVudCBBbmFseXNpcwpgYGB7cn0KYmlncmFtc19zZXBhcmF0ZWQgJT4lIAogIGZpbHRlcih3b3JkMSA9PSAibm90IikgJT4lIAogIGNvdW50KHdvcmQxLCB3b3JkMiwgc29ydCA9IFRSVUUpCmBgYAoKYGBge3J9CkFGSU5OIDwtIGdldF9zZW50aW1lbnRzKCJhZmlubiIpIAoKQUZJTk4KYGBgCgpgYGB7cn0Kbm90X3dvcmRzIDwtIGJpZ3JhbXNfc2VwYXJhdGVkICU+JSAKICByZW5hbWUod29yZCA9IHdvcmQyKSAlPiUgCiAgZmlsdGVyKHdvcmQxID09ICJub3QiKSAlPiUgCiAgaW5uZXJfam9pbihBRklOTikgJT4lIAogIGNvdW50KHdvcmQsIHNjb3JlLCBzb3J0ID0gVFJVRSkgJT4lIAogIHVuZ3JvdXAoKQogICAgCiAgICAKbm90X3dvcmRzCiAgCmBgYAoKCgpUaGUgMjAgc29yZHMgZm9sbG93ZWQgYnkgIm5vdCIgdGhhdCBoYWQgdGhlIGdyZWF0ZXN0IGNvbnRyaWJ1dGlvbiB0byBzZW50aW1lbnQgc2NvcmVzLCBpbiBlaXRoZXIgYSBwb3N0aXZpZSBvciBuZWdhdGl2ZSBkaXJlY3Rpb24uCmBgYHtyfQpub3Rfd29yZHMgJT4lIAogIG11dGF0ZShjb250cmlidXRpb24gPSBuICogc2NvcmUpICU+JSAKICBhcnJhbmdlKGRlc2MoYWJzKGNvbnRyaWJ1dGlvbikpKSAlPiUgCiAgaGVhZCgyMCkgJT4lIAogIG11dGF0ZSh3b3JkID0gcmVvcmRlcih3b3JkLCBjb250cmlidXRpb24pKSAlPiUgCiAgZ2dwbG90KGFlcyh3b3JkLCBuICogc2NvcmUsIGZpbGwgPSBuICogc2NvcmUgPiAwKSkgKwogIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpICsKICB4bGFiKCJXb3JkcyBwcmVjZWRlZCBieSBcIm5vdFwiIikgKwogIHlsYWIoIlNlbnRpbWVudCBzY29yZSAqIG51bWJlciBvZiBvY2N1cmVuY2VzIikgKwogIGNvb3JkX2ZsaXAoKQpgYGAKCmBgYHtyfQpuZWdhdGlvbl93b3JkcyA8LSBjKCJub3QiLCAibm8iLCAibmV2ZXIiLCAid2l0aG91dCIpCgpuZWdhdGVkX3dvcmRzIDwtIGJpZ3JhbXNfc2VwYXJhdGVkICU+JSAKICBmaWx0ZXIod29yZDEgJWluJSBuZWdhdGlvbl93b3JkcykgJT4lIAogIGlubmVyX2pvaW4oQUZJTk4sIGJ5ID0gYyh3b3JkMiA9ICJ3b3JkIikpICU+JSAKICBjb3VudCh3b3JkMSwgd29yZDIsIHNvcnQgPSBUUlVFKSAlPiUgCiAgdW5ncm91cCgpCgoKCm5lZ2F0ZWRfd29yZHMgJT4lIAogIGlubmVyX2pvaW4obm90X3dvcmRzKSAlPiUgCiAgbXV0YXRlKGNvbnRyaWJ1dGlvbiA9IG4gKiBzY29yZSkgJT4lIAogIGFycmFuZ2UoZGVzYyhhYnMoY29udHJpYnV0aW9uKSkpICU+JSAKICBoZWFkKDEyKSAlPiUgCiAgbXV0YXRlKHdvcmQgPSByZW9yZGVyKHdvcmQsIGNvbnRyaWJ1dGlvbikpICU+JSAKICBnZ3Bsb3QoYWVzKHdvcmQsIG4gKiBzY29yZSwgZmlsbCA9IG4gKiBzY29yZSA+IDApKSArCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIHhsYWIoIldvcmRzIHByZWNlZGVkIGJ5IFwibm90XCIiKSArCiAgeWxhYigiU2VudGltZW50IHNjb3JlICogbnVtYmVyIG9mIG9jY3VyZW5jZXMiKSArCiAgY29vcmRfZmxpcCgpICsKICBmYWNldF93cmFwKH53b3JkMSkKYGBgCgoKQW5hbHl6aW5nIG5ldHdvcmtzIHdpdGggaWdyYXBoLgoKQ3JlYXRlIGFuIGlncmFwaCBvYmplY3QgZnJvbSB0aWR5IGRhdGEgdXNpbmcgdGhlICdncmFwaF9mcm9tX2RhdGFfZnJhbWUoKScgZnVuY3Rpb24sIHdoaWNoIHRha2VzIGEgZGF0YSBmcmFtZSBvZiBlZGdlcyB3aXRoIGNvbHVtbnMgZm9yICJmcm9tIiwgInRvIiBhbmQgZWRnZSBhdHRyaWJ1dGVzIChpbiB0aGlzIGNhc2UgbikuCgpgYGB7cn0KbGlicmFyeShpZ3JhcGgpCgpiaWdyYW1fY291bnRzCgoKYmlncmFtX2dyYXBoIDwtIGJpZ3JhbV9jb3VudHMgJT4lIAogIGZpbHRlcihuID4gMjApICU+JSAKICBncmFwaF9mcm9tX2RhdGFfZnJhbWUoKQoKYmlncmFtX2dyYXBoCiAgCmBgYAoKQ29tbW9uZSBiaWdyYW1zIGluIFByaWRlIGFuZCBQcmVqdWRpY2UsIHNob3dpbmcgdGhvc2UgdGhhdCBvY2N1cnJlZCBtb3JlIHRoYXRuIDIwIHRpbWVzIGFuZCB3aGVyZSBuZWlnaHRlciB3b3JkIHdhcyBhIHN0b3Agd29yZApgYGB7cn0KbGlicmFyeShnZ3JhcGgpCgpzZXQuc2VlZCgyMDE3KQoKZ2dyYXBoKGJpZ3JhbV9ncmFwaCwgbGF5b3V0ID0gImZyIikgKwogIGdlb21fZWRnZV9saW5rKCkgKwogIGdlb21fbm9kZV9wb2ludCgpICsKICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWwgPSBuYW1lKSwgdmp1c3QgPSAxLCBoanVzdCA9IDEpCgoKYGBgCkNvbW1vbiBiaWdyYW1zIGluIFByaWRlIGFuZCBQcmVqdWRpY2UsIHdpdGggc29tZSBwb2xpc2hpbmcKYGBge3J9CnNldC5zZWVkKDIwMTYpCgoKYSA8LSBncmlkOjphcnJvdyh0eXBlID0gImNsb3NlZCIsIGxlbmd0aCA9IHVuaXQoLjE1LCAiaW5jaGVzIikpCgpnZ3JhcGgoYmlncmFtX2dyYXBoLCBsYXlvdXQgPSAiZnIiKSArCiAgZ2VvbV9lZGdlX2xpbmsoYWVzKGVkZ2VfYWxwaGEgPSBuKSwgc2hvdy5sZWdlbmQgPSBGQUxTRSwKICAgICAgICAgICAgICAgICBhcnJvdyA9IGEsIGVuZF9jYXAgPSBjaXJjbGUoLjA3LCAnaW5jaGVzJykpICsKICBnZW9tX25vZGVfcG9pbnQoY29sb3IgPSAibGlnaHRibHVlIiwgc2l6ZSA9IDUpICsKICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWwgPSBuYW1lKSwgdmp1c3QgPSAxLCBoanVzdCA9IDEpICsKICB0aGVtZV92b2lkKCkKCgoKYGBgCgojVG9waWNtb2RlbHMgcGFja2FnZQpgYGB7cn0KbGlicmFyeSh0bSkKCmRhdGEoIkFzc29jaWF0ZWRQcmVzcyIsIHBhY2thZ2UgPSAidG9waWNtb2RlbHMiKQoKQXNzb2NpYXRlZFByZXNzCmBgYAoKYGBge3J9CnRlcm1zIDwtIFRlcm1zKEFzc29jaWF0ZWRQcmVzcykKaGVhZCh0ZXJtcykKYGBgCgpgYGB7cn0KIyBsaWJyYXJ5KHRpZHl2ZXJzZSkKIyBsaWJyYXJ5KHRpZHl0ZXh0KQoKYXBfdGQgPC0gdGlkeShBc3NvY2lhdGVkUHJlc3MpCmFwX3RkCmBgYAoKYGBge3J9CmFwX3NlbnRpbWVudHMgPC0gYXBfdGQgJT4lIAogIGlubmVyX2pvaW4oZ2V0X3NlbnRpbWVudHMoImJpbmciKSwgYnkgPSBjKCB0ZXJtID0gIndvcmQiKSkKCgphcF9zZW50aW1lbnRzCmBgYAoKYGBge3J9CiMgZ2dwbG90MgoKYXBfc2VudGltZW50cyAlPiUgCiAgY291bnQoc2VudGltZW50LCB0ZXJtLCB3dCA9IGNvdW50KSAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBmaWx0ZXIobj49IDIwMCkgJT4lIAogIG11dGF0ZShuID0gaWZlbHNlKHNlbnRpbWVudCA9PSAibmVnYXRpdmUiLCAtbiwgbikpICU+JSAKICBtdXRhdGUodGVybSA9IHJlb3JkZXIodGVybSwgbikpICU+JSAKICBnZ3Bsb3QoYWVzKHRlcm0sIG4sIGZpbGwgPSBzZW50aW1lbnQpKSArCiAgZ2VvbV9jb2woKSArCiAgeWxhYigiQ29udHJpYnV0aW9uIHRvIHNlbnRpbWVudCIpICsKICBjb29yZF9mbGlwKCkKYGBgCgpgYGB7cn0KbGlicmFyeShtZXRob2RzKQoKZGF0YSgiZGF0YV9jb3JwdXNfaW5hdWd1cmFsIiwgcGFja2FnZSA9ICJxdWFudGVkYSIpCmluYXVnX2RmbSA8LSBxdWFudGVkYTo6ZGZtKGRhdGFfY29ycHVzX2luYXVndXJhbCwgdmVyYm9zZSA9IEZBTFNFKQoKaW5hdWdfZGZtCmBgYAoKYGBge3J9CmluYXVnX3RkIDwtIHRpZHkoaW5hdWdfZGZtKQppbmF1Z190ZApgYGAKCmBgYHtyfQoKaW5hdWdfdGZfaWRmIDwtIGluYXVnX3RkICU+JQogIGJpbmRfdGZfaWRmKHRlcm0sIGRvY3VtZW50LCBjb3VudCkgJT4lIAogIGFycmFuZ2UoZGVzYyh0Zl9pZGYpKQoKaW5hdWdfdGZfaWRmCiAgCmBgYApgYGB7cn0KCmluYXVnX3RmX2lkZiAlPiUgCiAgZmlsdGVyKGRvY3VtZW50ICVpbiUgYygiMTg2MS1MaW5jb2xuIiwgIjE5MzMtUm9vc2V2ZWx0IiwgIjE5NjEtS2VubmVkeSIsICIyMDA5LU9iYW1hIikpICU+JSAKICBnZ3Bsb3QoYWVzKHJlb3JkZXIodGVybSx0Zl9pZGYpLCB0Zl9pZGYsIGZpbGwgPSB0Zl9pZGYpKSArCiAgZ2VvbV9jb2woKSsKICBjb29yZF9mbGlwKCkrCiAgZmFjZXRfd3JhcCh+ZG9jdW1lbnQpCmBgYAoK